/*
 * Copyright 2024 ywcai
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

`include "defines.v"
`timescale 1ns/1ps

module id(
	input	wire[`MemAddrBus]	pc_i,
	input	wire[`InstDataBus]	inst_i,
    input   wire[2:0]           mux_imm_src_i,

	output	wire[`MemAddrBus]	pc_o,
	output	wire[`InstDataBus]	inst_o,
    output	wire[6:0]			opcode_o,
    output	wire[2:0] 			funct3_o,
    output	wire[6:0] 			funct7_o,
	output	wire				rs1_en_o,
	output	wire[`RegAddrBus]	rs1_addr_o,
	output	wire				rs2_en_o,
	output	wire[`RegAddrBus]	rs2_addr_o,
    output  wire[`RegAddrBus]   regwrite_addr_o,
	output	wire[`RegDataBus]	rs1_imm_value_o,
	output	wire[`RegDataBus]	rs2_imm_value_o,
	output	wire[`TrapBus]		trap_code_o
	);

    wire[6:0] opcode = inst_i[6:0];
    wire[2:0] funct3 = inst_i[14:12];
    wire[6:0] funct7 = inst_i[31:25];

	assign pc_o = pc_i;
	assign inst_o = inst_i;
	assign opcode_o = opcode;
	assign funct3_o = funct3;
	assign funct7_o = funct7;

	wire i_inst_i_type	= (opcode == `I_TYPE);
	wire i_inst_iw_type	= (opcode == `IW_TYPE);
	wire i_inst_b_type	= (opcode == `B_TYPE);
	wire i_inst_r_type	= (opcode == `R_TYPE);
	wire i_inst_rw_type	= (opcode == `RW_TYPE);
	wire i_inst_auipc	= (opcode == `INST_AUIPC);
	wire i_inst_ecall	= (inst_i == `INST_ECALL);
	wire i_inst_ebreak	= (inst_i == `INST_EBREAK);
	wire i_inst_mret	= (inst_i == `INST_MRET);
	wire i_inst_load	= (opcode == `INST_LOAD);
	wire i_inst_store	= (opcode == `INST_STORE);
	wire i_inst_jal		= (opcode == `INST_JAL);
	wire i_inst_jalr	= (opcode == `INST_JALR);
	wire i_inst_lui		= (opcode == `INST_LUI);
	wire i_inst_sys		= (opcode == `INST_SYS);
	wire i_inst_csrrwi	= (i_inst_sys & (funct3 == `FUNCT3_101));
	wire i_inst_csrrsi	= (i_inst_sys & (funct3 == `FUNCT3_110));
	wire i_inst_csrrci	= (i_inst_sys & (funct3 == `FUNCT3_111));

	wire i_inst_illegal = !(i_inst_load | i_inst_store
		| i_inst_i_type | i_inst_iw_type
		| i_inst_r_type | i_inst_rw_type
		| i_inst_jal | i_inst_jalr  | i_inst_b_type
		| i_inst_lui | i_inst_auipc
		| i_inst_sys);

	assign rs1_en_o = `ENABLE;
	assign rs2_en_o = `ENABLE;

	assign rs1_addr_o = (i_inst_lui | i_inst_auipc | i_inst_jal
		| i_inst_ecall | i_inst_ebreak | i_inst_mret
		| i_inst_csrrwi | i_inst_csrrsi | i_inst_csrrci)
		? `REG_ZERO_ADDR : inst_i[19:15];

	assign rs2_addr_o = (i_inst_lui | i_inst_auipc | i_inst_jal
		| i_inst_jalr | i_inst_load | i_inst_i_type | i_inst_sys)
		? `REG_ZERO_ADDR : inst_i[24:20];

	assign regwrite_addr_o = (i_inst_b_type | i_inst_store
		| i_inst_ecall | i_inst_ebreak | i_inst_mret)
		? `REG_ZERO_ADDR : inst_i[11:7];

	assign rs1_imm_value_o = i_inst_lui ? `ZERO : {59'h0, inst_i[19:15]};

	imm_src i_imm_src(
		.inst_i(inst_i),
		.mux_imm_src_i(mux_imm_src_i),
		.imm_data_o(rs2_imm_value_o)
	);

	// 2 ebreaks for placeholder for inst/data ebreak
    assign trap_code_o = {{3{1'b0}}, i_inst_mret, i_inst_ebreak, i_inst_ebreak,
		i_inst_ecall, i_inst_illegal, {8{1'b0}}};

endmodule
	
	
